#include "Astronomy/CDynamicalTime.hpp"
#include "Astronomy/Celestial/Sun.hpp"
#include "Astronomy/Constant.hpp"
#include "SexagenaryCycle.hpp"

#include <cmath>

std::pair<HeavenlyStem, EarthlyBranch> SexagenaryCycle::FromOrdinal(unsigned int ordinal) noexcept
{
    return {(HeavenlyStem)(ordinal % HeavenlyStemMax), (EarthlyBranch)(ordinal % EarthlyBranchMax)};
}
int SexagenaryCycle::ToOrdinal(HeavenlyStem heanvenlyStem, EarthlyBranch earthlyBranch) noexcept
{
    const int unsigned hs = static_cast<unsigned int>(heanvenlyStem);
    const int unsigned eb = static_cast<unsigned int>(earthlyBranch);

    // 计算差值 取绝对值;
    int difference        = eb - hs;
    // 用于判断规则 : 天干与地支相差是2的倍数,或者0;
    if ((difference % 2) != 0)
    {
        // throw "Sexagenary Cycle：format Error!";
        return -1;
    }
    // 差值小于等于0 需要加上12，用来保证差值为正数，否则结果不完全准确;
    if (difference <= 0)
        difference += EarthlyBranchMax;

    return ((6 - (difference / 2)) * 10 + hs) % SexagenaryCycleMaximum;
}
SexagenaryCycleDate::SexagenaryCycleDate(double jd, double localLongitude)
{
    // 过滤重复计算;
    if ((dateJulianDay == jd) && (this->dateLocalLongitude == localLongitude))
        return;
    // 保存当前参数;
    this->dateJulianDay      = jd;
    this->dateLocalLongitude = localLongitude;
    // 力学时;
    double localJd           = jd + CDynamicalTime::TDUT(jd);
    // 此刻太阳视黄经
    double w                 = CelestialSun::ApparentEclipticLongitude(localJd / 36525.0, -1);
    // 1984年立春起算的节气数(不含中气);
    int k                    = (int)floor((w / Constants::PI2 * 360.0 + 45.0 + 15.0 * 360.0) / 30.0);
    // 本地真太阳时(使用低精度算法计算时差)
    jd += CelestialSun::CalcTimeDifferenceLow(localJd / 36525.0) + localLongitude / Constants::PI / 2.0;

    //  保存真太阳时;
    this->apparentSolarTimeJd = jd;

    // 转为前一日23点起算(原jd为本日中午12点起算)
    jd += 13.0 / 24.0;

    double D = floor(jd), SC = floor((jd - D) * 12.0); // 日数与时辰

    // 干支纪年;
    this->scYear  = (unsigned int)floor(k / 12.0 + 6000000);
    this->scMonth = (unsigned int)(k + 2 + 60000000);
    this->scDay   = (unsigned int)(D - 6 + 9000000);
    this->scHour  = (unsigned int)((D - 1) * 12 + 90000000 + SC);
}